package ldh.im.fxgui;

import de.saxsys.svgfx.core.SVGParser;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.text.Font;
import javafx.scene.text.HitInfo;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.scene.web.HTMLEditor;
import javafx.scene.web.WebView;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Callback;
import javafx.util.Duration;
import ldh.im.fxbase.component.BigDataListView;
import ldh.im.fxbase.richtext.ImTextArea;
import ldh.im.fxbase.util.HtmlEditorUtil;
import ldh.im.fxbase.util.RegionUtil;
import ldh.im.fxbase.util.StageUtil;
import ldh.im.fxbase.util.ThreadPoolUtil;
import ldh.im.fxbase.cell.ObjectListCell;
import ldh.im.fxbase.cache.SoftId;
import ldh.im.fxgui.controller.MessageController;
import ldh.im.fxgui.util.*;
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.fxmisc.richtext.*;
import org.kordamp.ikonli.fontawesome.FontAwesome;
import org.kordamp.ikonli.javafx.FontIcon;

import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Supplier;

public class SampleDemo extends Application {

    private VBox leftPane = new VBox(10);
    private StackPane contentPane = new StackPane();
    private Font font = Font.loadFont(SampleDemo.class.getResource("/font/OpenMoji-Color.ttf").toExternalForm(), 26);
    private Map<String, TitledPane> groupMap = new HashMap();
    private WeakHashMap<Integer, Node> weakMap = new WeakHashMap<>();

    @Override
    public void start(Stage primaryStage) throws Exception {
        ImTextArea imTextArea;
        StageUtil.setStage(primaryStage);
        leftPane.getStyleClass().add("demo-left-pane");
        contentPane.getStyleClass().add("demo-content-pane");

        BorderPane borderPane = new BorderPane();
        borderPane.setTop(buildTop());
        ScrollPane leftContainer = new ScrollPane(leftPane);
        leftContainer.setFitToHeight(true);
        leftContainer.setFitToWidth(true);
        borderPane.setLeft(leftContainer);

        ScrollPane contentContainer = new ScrollPane(contentPane);
        contentContainer.setFitToHeight(true);
        contentContainer.setFitToWidth(true);
        borderPane.setCenter(contentContainer);

        buildTests();

        primaryStage.setOnCloseRequest(e->{
            ThreadPoolUtil.close();
        });

        Scene scene = new Scene(borderPane, 800, 600);
//        scene.getStylesheets().add(this.getClass().getResource("/css/init.css").toExternalForm());
        scene.getStylesheets().add(this.getClass().getResource("/css/demo.css").toExternalForm());
        scene.getStylesheets().add(this.getClass().getResource("/css/var.css").toExternalForm());
        scene.getStylesheets().add(this.getClass().getResource("/css/main.css").toExternalForm());
        scene.getStylesheets().add(this.getClass().getResource("/css/tabpane.css").toExternalForm());
        scene.getStylesheets().add(this.getClass().getResource("/css/window-scrollbar.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void buildTests() {
        addTest("ListView Demo", ()->{
            BigDataListView<Activity> listView = new BigDataListView();
            Callback<Activity, Node> callback = listCell();
            listView.setCellCallback(callback);
            listView.setCache(false);
            listView.getItems().addAll(loadActivityList(5000));
            return listView;
        });

        addTest("ListView Cache Demo", ()->{
            ListView<Activity> listView = new ListView();
            Callback<Activity, Node> callback = listCell();
            listView.setCellFactory(new ObjectListCell<>(activity -> {
                Node node = weakMap.get(activity.getId());
                if (node != null) {
                    if (weakMap.size() > 100) {
                        cleanWeakMap();
                    }
                    System.out.println("cache node: " + activity.getId() + ", size:" + weakMap.size());
                    return node;
                }
                Node newNode = callback.call(activity);
                weakMap.put(activity.getId(), newNode);
                return newNode;
            }));
            listView.getItems().addAll(loadActivityList(5000));
            return listView;
        });

        addTest("ListView No Cache Demo", ()->{
            ListView<Activity> listView = new ListView();
            Callback<Activity, Node> callback = listCell();
            listView.setCellFactory(new ObjectListCell<>(activity -> {
                Node newNode = callback.call(activity);
                return newNode;
            }));
            listView.getItems().addAll(loadActivityList(5000));
            return listView;
        });

        addTest("Flowless Demo", ()->{
            List<Activity> activities = loadActivityList(5000);
            ObservableList<Activity> items = FXCollections.observableList(activities);
            Callback<Activity, Node> callback = listCell();
            VirtualFlow<Activity, ?> flow = VirtualFlow.createVertical(
                    items,
                    aitivity -> {
                        Node node = callback.call(aitivity);
                        return Cell.wrapNode(node);
                    });
            VirtualizedScrollPane stackPane = new VirtualizedScrollPane(flow);
            return stackPane;
        });

        addTest(false,"HTMLEditor Demo", ()->{
            Button b = new Button("add image");
            HTMLEditor textArea = HtmlEditorUtil.createHtmlEditor();
            Label l = new Label(); l.setText("<h2>asfdasfa</h2>");
            VBox vBox = new VBox(b, textArea, l);
            b.setOnAction(e->{
                String t = "file://F:\\other\\aaa\\22.png";
                String tt = textArea.getHtmlText();
                String txt = tt + String.format("<img src=\"%s\"/>", t);
                System.out.println("txt:" + txt);
                textArea.setHtmlText(txt);
            });

            return vBox;
        });

        addTest("HTMLEditor Demo", ()->{
            VBox content = new VBox();

            VBox contentPanet = new VBox();
            ScrollPane scrollPane = new ScrollPane();
            scrollPane.setContent(contentPanet);
            VBox.setVgrow(scrollPane, Priority.ALWAYS);

            HBox hBox = new HBox(5);
            Button b = new Button("add");
            HTMLEditor textArea = new HTMLEditor();
            textArea.setPrefHeight(200);

            hBox.getChildren().addAll(textArea, b);

            b.setOnAction(e->{
                HTMLEditor htmlEditor = HtmlEditorUtil.createHtmlEditor();
                String txt = textArea.getHtmlText();
                htmlEditor.setHtmlText(txt);
                htmlEditor.setPrefWidth(300);
                htmlEditor.setPrefHeight(50);
                double h = RegionUtil.calcStringHeight(txt, 300);
                htmlEditor.setPrefHeight(h-20);
                contentPanet.getChildren().add(htmlEditor);

            });

            content.getChildren().addAll(scrollPane, hBox);
            return content;
        });

        addTest(false, "HtmlEditor Demo2 height", ()->{
            long start = System.currentTimeMillis();
            VBox contentPanet = new VBox();
            ScrollPane scrollPane = new ScrollPane();
            scrollPane.setContent(contentPanet);
            String str ="I have a HTML5 UI and a Java backend and want to avoid rebuilding the HTML ui in plain java, so my idea was to run" +
                    " a local webserver and use a webview to render it in a \"native\" window. The solution seems to be to use a JavaFX WebView " +
                    "which can be embedded in swing. On the paper it sounds great (especially since they claim to use WebKit which has a " +
                    "significantly better performance for my UI in Chrome / Safari).";
            for(int i=0; i<300; i++) {
                HTMLEditor htmlEditor = HtmlEditorUtil.createHtmlEditor();
                htmlEditor.setHtmlText(str);
                htmlEditor.setPrefSize(400, 100);

                Text textHolder = new Text();
                textHolder.setWrappingWidth(400);
//                double h2 = RegionUtil.calcStringHeight(str, 400);
                contentPanet.getChildren().add(htmlEditor);
                textHolder.setText(str);
                double h = textHolder.layoutBoundsProperty().get().getHeight();
                System.out.println("h11:" + h);
//                textHolder.layoutBoundsProperty().addListener((ob, o, newValue)->{
//                    double h = textHolder.getLayoutBounds().getHeight() + 10;
//                    htmlEditor.setPrefHeight(h);
//                    System.out.print("h:" + h);
//                });
                htmlEditor.setPrefHeight(h);
            }
            System.out.println("htmlEdit time:" + (System.currentTimeMillis() - start));
            return scrollPane;
        });

        addTest(false,"RichTextArea Demo", ()->{
            long start = System.currentTimeMillis();
            VBox contentPanet = new VBox(5);
            ScrollPane scrollPane = new ScrollPane();
            scrollPane.setContent(contentPanet);
            String str ="I have a HTML5 UI and a Java backend and want to avoid rebuilding the HTML ui in plain java, so my idea was to run" +
                    " a local webserver and use a webview to render it in a \"native\" window. The solution seems to be to use a JavaFX WebView " +
                    "which can be embedded in swing. On the paper it sounds great (especially since they claim to use WebKit which has a " +
                    "significantly better performance for my UI in Chrome / Safari).";
            for(int i=0; i<300; i++) {
                CodeArea codeArea = new CodeArea();
                codeArea.setEditable(true);
                codeArea.setWrapText(true);
                codeArea.replaceText(0, 0, str);
                codeArea.setPrefWidth(400);
                contentPanet.getChildren().add(codeArea);
            }
            System.out.println("JEditorPane time:" + (System.currentTimeMillis() - start));
            return scrollPane;
        });

        addTest(false, "WebView Demo", ()->{
            long start = System.currentTimeMillis();
            VBox contentPanet = new VBox(5);
            ScrollPane scrollPane = new ScrollPane();
            scrollPane.setContent(contentPanet);
            String str ="I have a HTML5 UI and a Java backend and want to avoid rebuilding the HTML ui in plain java, so my idea was to run" +
                    " a local webserver and use a webview to render it in a \"native\" window. The solution seems to be to use a JavaFX WebView " +
                    "which can be embedded in swing. On the paper it sounds great (especially since they claim to use WebKit which has a " +
                    "significantly better performance for my UI in Chrome / Safari).";
            for(int i=0; i<300; i++) {
                WebView webView = new WebView();
                webView.setPrefWidth(400);
                webView.setPrefHeight(100);
                webView.getEngine().loadContent(str);
                contentPanet.getChildren().add(webView);
            }
            System.out.println("WebView time:" + (System.currentTimeMillis() - start));
            return scrollPane;
        });

        addTest("TextArea Demo", ()->{
            long start = System.currentTimeMillis();
            VBox contentPanet = new VBox(5);
            ScrollPane scrollPane = new ScrollPane();
            scrollPane.setContent(contentPanet);
            String str ="I have a HTML5 UI and a Java backend and want to avoid rebuilding the HTML ui in plain java, so my idea was to run" +
                    " a local webserver and use a webview to render it in a \"native\" window. The solution seems to be to use a JavaFX WebView " +
                    "which can be embedded in swing. On the paper it sounds great (especially since they claim to use WebKit which has a " +
                    "significantly better performance for my UI in Chrome / Safari).";

            for(int i=0; i<300; i++) {
                Text text = new Text(str);
                text.setFont(Font.font(21));
                text.setWrappingWidth(400);
                double height = text.getBoundsInLocal().getHeight();
                TextArea textArea = new TextArea(str);
                textArea.setWrapText(true);
                textArea.setPrefSize(400, height);
                contentPanet.getChildren
                        ().add(textArea);
            }
            System.out.println("TextArea time:" + (System.currentTimeMillis() - start));
            return scrollPane;
        });

        addTest("textArea height", ()->{
            Pane root = new Pane();

             ScrollPane scroller;
             Pane content = new Pane();

             TextField textF = new TextField();
             TextArea textA = new TextArea();

             Text textHolder = new Text();
            content.setMinSize(300, 300);
            textF.setPrefSize(260, 40);
            textF.setLayoutX(20);
            textF.setLayoutY(20);
            textA.setPrefSize(260, 200);
            textA.setLayoutX(20);
            textA.setLayoutY(80);

//            textA.getStylesheets().add(getClass().getResource("texta.css").toExternalForm());
            content.getChildren().addAll(textA,textF);

            /*************************@Uluk Biy Code**************************/
            textA.setWrapText(true);
            textHolder.textProperty().bind(textA.textProperty());
            textHolder.layoutBoundsProperty().addListener((ob, o, newValue)->{
                textA.setPrefHeight(textHolder.getLayoutBounds().getHeight() + 10);
            });
            /****************************************************************/

            scroller = new ScrollPane(content);
            scroller.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
            scroller.setPrefSize(300, 316);

            return scroller;
        });

        addTest("TextField Font Demo", ()->{
            VBox vBox = new VBox(10);
            TextField textField = new TextField("Look! That's a coloured emoji! \uD83D\uDE04");
            vBox.getChildren().addAll(textField, new Label("Look! That's a coloured emoji! \uD83D\uDE04"));

            Text text = new Text("Box with long description that should be wrapped");
            text.textProperty().bind(textField.textProperty());
            text.setCaretPosition(5);
            TextFlow textFlow = new TextFlow(text) {
                @Override
                protected void layoutChildren() {
                    super.layoutChildren();

                    double maxChildWidth = 0;
                    for (Node child : getManagedChildren()) {
                        double childWidth = child.getLayoutBounds().getWidth();
                        maxChildWidth = Math.max(maxChildWidth, childWidth);
                    }
                    double insetWidth = getInsets().getLeft() + getInsets().getRight();
                    double adjustedWidth = maxChildWidth + insetWidth;

                    setMaxWidth(adjustedWidth);
                }
            };

            textFlow.setStyle("-fx-background-color: red");
            textFlow.setMaxWidth(Control.USE_PREF_SIZE);
            textFlow.setMaxHeight(Control.USE_PREF_SIZE);

            Button unclamp = new Button("Unclamp max width");
            unclamp.setOnAction(e -> textFlow.setMaxWidth(Double.MAX_VALUE));

            StackPane wrappedText = new StackPane(textFlow);

            VBox.setVgrow(wrappedText, Priority.ALWAYS);
            vBox.getChildren().addAll(unclamp, wrappedText);
            return vBox;
        });

        addTest("TextFlow Demo", ()->{
            ScrollPane scrollPane = new ScrollPane();
            String str ="I have a HTML5 UI and a Java backend and want to avoid rebuilding the HTML ui in plain java, so my idea was to run" +
                    " a local webserver and use a webview to render it in a \"native\" window. The solution seems to be to use a JavaFX WebView " +
                    "which can be embedded in swing. On the paper it sounds great (especially since they claim to use WebKit which has a " +
                    "significantly better performance\uD83D\uDC68 for my UI in Chrome \uD83D\uDC68\u200D\uD83D\uDCBB / Safari)." +
                    "ABC \uD83D\uDC68\u200D\uD83D\uDCBB \uD83E\uDD76  XYZ \uD83D\uDCBB \uD83D\uDC68 \uD83E\uDDDF Lorem \uE380  ipsum \uD83D\uDCAF";
            Text t = buildText(null);
            t.setText(str);
            t.setSelectionFill(Color.RED);
//            t.setWrappingWidth(400);

            Text t2 = buildText(t);
            t2.setText(str);
            t2.setSelectionFill(Color.RED);
//            t2.setWrappingWidth(400);
            Label label = new Label("\uD83D\uDC31");
            label.setStyle("-fx-text-fill: red; -fx-fill: red");
            TextFlow textFlow = new TextFlow(t, t2, label);
            StackPane stackPane = new StackPane(textFlow);

            textFlow.setOnDragDetected(e->{
                System.out.println(String.format("x:%s, y:%s", e.getX(), e.getY()));
                HitInfo hitInfo = textFlow.hitTest(new Point2D(e.getX(), e.getY()));
                t.setCaretPosition(hitInfo.getCharIndex());
                t.setSelectionStart(hitInfo.getCharIndex());
                t.setSelectionEnd(hitInfo.getCharIndex());
                t2.setSelectionStart(hitInfo.getCharIndex());


            });
            textFlow.setOnMouseDragged(e->{
                HitInfo hitInfo = textFlow.hitTest(new Point2D(e.getX(), e.getY()));
                t.setSelectionEnd(hitInfo.getCharIndex());
                t2.setSelectionEnd(hitInfo.getCharIndex());

                PathElement[] pathElements = textFlow.caretShape(hitInfo.getCharIndex(), true);
                Path path = new Path(pathElements);
                stackPane.getChildren().clear();
                stackPane.getChildren().addAll(path, textFlow);
            });

            textFlow.focusedProperty().addListener((InvalidationListener)l->{
                if (!textFlow.isFocused()) {
                    t.setSelectionStart(0);
                    t.setSelectionEnd(0);

                    t2.setSelectionStart(0);
                    t2.setSelectionEnd(0);
                }
            });

            scrollPane.setFitToWidth(true);
            scrollPane.setContent(stackPane);
            return scrollPane;
        });

        addTest("Svg Demo", ()->{
            FlowPane flowPane = new FlowPane(20, 20);
            try {
                Enumeration<URL> urls = this.getClass().getClassLoader().getResources("emoji-svg/");
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    String srcFile = url.getPath();
                    File ff = new File(srcFile);
                    if (ff.isDirectory()) {
                        File[] ffs = ff.listFiles();
                        int t = 0;
                        for(File f : ffs) {
//                            String str = Files.readAllLines(f.toPath()).stream().collect(Collectors.joining(""));
                            try {
                                SVGParser parser = new SVGParser();
                                parser.parse(f);
                                flowPane.getChildren().add(parser.getResult());
//                                SVGContent svgContent = SVGLoader.load(f.getPath());
//                                flowPane.getChildren().add(svgContent);
                                t++;
                            } catch (Exception e) {
                                System.out.println("error file:" + f.getName());
                                e.printStackTrace();
                            }
                        }
                        System.out.println("size:" + ffs.length +":: l:" + t);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            ScrollPane scrollPane = new ScrollPane(flowPane);
            scrollPane.setFitToWidth(true);
            return scrollPane;
        });

        addTest(false,"TextArea Emoji Demo", ()->{
            TextArea textArea = new TextArea();
            textArea.setPrefHeight(60);
            textArea.setPrefRowCount(5);
            textArea.setStyle("-fx-fill: yellow");

            TextArea textArea1 = new TextArea("\uD83D\uDC31");
            Label label = new Label("ABC \uD83D\uDC68\u200D\uD83D\uDCBB &#x1F976;  XYZ #x1F4BB; \uD83D\uDC31;\uD83D\uDC44");
            textArea1.setFont(font);
            label.setFont(font);

            Button e3 = new Button();
            e3.setText("\uD83D\uDC44");
            e3.setStyle("-fx-text-fill: #ff0000;");

            CodeArea codeArea = new CodeArea();
            codeArea.setWrapText(true);
            codeArea.setEditable(true);
            codeArea.insertText(0, "ABC \uD83D\uDC68\u200D\uD83D\uDCBB &#x1F976;  XYZ #x1F4BB; \uD83D\uDC31;\uD83D\uDC44");

            Text text = new Text();
            text.setWrappingWidth(400);
            text.setFont(font);
            text.setText("ABC \uD83D\uDC68\u200D\uD83D\uDCBB &#x1F976;  XYZ #x1F4BB; \uD83D\uDC31;\uD83D\uDC44");

            VBox vBox = new VBox(10, textArea, textArea1, label, e3, codeArea, text);
            ScrollPane scrollPane = new ScrollPane(vBox);
            scrollPane.setFitToWidth(true);
            scrollPane.setFitToHeight(true);
            return scrollPane;
        });

        addTest(false,"WebView Demo", ()->{
            VBox vBox = new VBox(10);
            WebView webView = new WebView();

            final String cssLink = MessageController.class.getResource("/css/html.css").toExternalForm();
            final String jsLink = MessageController.class.getResource("/js/html.js").toExternalForm();
            final String initHtml = "<link rel=\"stylesheet\" href=\"" + cssLink +"\" /><br/><script type=\"text/javascript\" src=\"" + jsLink + "\"></script>";

            String styleAndJs = initHtml;
            String initHtmls=String.format("<html dir=\"ltr\"><head>%s</head><body contenteditable=\"true\"><div id=\"content\"/></body></html>", styleAndJs);
            webView.getEngine().loadContent(initHtmls);
            webView.getEngine().setJavaScriptEnabled(true);

            Button b = new Button("add img");
            b.setOnAction(e->{
                String img = MessageController.class.getResource("/imgs/head.jpg").toExternalForm();
                String tt = String.format("<img src='%s' class='emoji'/>", img) ;

                try {
                    webView.getEngine().executeScript("insertHtml(\"" + tt + "\")");
                } catch (Exception js) {
                    String t = HtmlEditorUtil.getWebViewHtml(webView);
                }
            });

            Button b5 = new Button("add icon");
            b5.setOnAction(e->{
                UiUtil.getEmojiPane().show(b5, emojiSpan->{
                    HtmlEditorUtil.insertHtml(webView, emojiSpan);
                });
            });

            Button b2 = new Button("get data");
            b2.setOnAction(e->{
                String html = (String) webView.getEngine().executeScript("document.documentElement.outerHTML");
                System.out.println("text:" + webView.getEngine().getDocument().getTextContent());
                System.out.println("string:" + html);
            });
            Button b3 = new Button("add cart");
            b3.setOnAction(e->{
                webView.getEngine().executeScript("placeCaretAtEnd(document.getElementById(\"content\"))");
            });
            HBox hBox = new HBox(10, b, b5, b2, b3);
            vBox.getChildren().addAll(webView, hBox);
            return vBox;
        });

        addTest(false,"HtmlEditor Demo", ()->{
            VBox vBox = new VBox(10);
            HTMLEditor webView = new HTMLEditor();

            Button b2 = new Button("get data");
            b2.setOnAction(e->{
                String html = webView.getHtmlText();
                System.out.println("string:" + html);
            });

            Button b3 = new Button("add HTMLEditor");
            b3.setOnAction(e->{
                ThreadPoolUtil.submit(()->{
                    HTMLEditor webView2 = new HTMLEditor();
                    Platform.runLater(()->vBox.getChildren().add(webView2));
                });
            });
            vBox.getChildren().addAll(webView, b2, b3);
            return vBox;
        });

        addTest(true,"WebView load Demo", ()->{
            VBox vBox = new VBox(10);
            WebView webView = new WebView();
            String url = "F:/download/openmoji-font/demo.html";
            webView.getEngine().load("file://" +url);

            Button b2 = new Button("get data");
            b2.setOnAction(e->{
                String html = HtmlEditorUtil.getWebViewHtml(webView);
                System.out.println("string:" + html);
            });
            HTMLEditor htmlEditor = HtmlEditorUtil.createHtmlEditor();
            try {
                String t = Files.readString(Paths.get(url));
                System.out.println("t:" + t);
                htmlEditor.setHtmlText(t);
            } catch (Exception e) {
                e.printStackTrace();
            }

            vBox.getChildren().addAll(webView, b2, htmlEditor);
            return vBox;
        });

        addTest("Capture Screen Demo", ()->{
            VBox vBox = new VBox(10);
            WebView webView = new WebView();
            webView.getEngine().loadContent(UiUtil.getInitWebViewHtml());

            Button b1 = new Button("截屏111");
            b1.setOnAction(e->{
                UiUtil.getScreenCapturePane().show(str->{
                    HtmlEditorUtil.insertHtml(webView, str);
                });
            });

            Button b2 = new Button("获取数据");
            b2.setOnAction(e->{
                System.out.println(HtmlEditorUtil.getWebViewHtml(webView));
            });

            vBox.getChildren().addAll(webView, b1, b2);
            webView.getEngine().loadContent(UiUtil.getInitWebViewHtml());
            return vBox;
        });

        addTest("New Stage Demo", ()->{
            VBox vBox = new VBox();
            Button b1 = new Button("New Stage");
            Button b2 = new Button("截屏111");
            b2.setOnAction(e1->{
                UiUtil.getScreenCapturePane().show(str->{
                    System.out.println(str);
//                        HtmlEditorUtil.insertHtml(webView, str);
                });
            });

            b1.setOnAction(e->{
                Stage stage = new Stage();
                stage.initStyle(StageStyle.TRANSPARENT);
                Label label = new Label();
                Button b3 = new Button("截屏111");
                b3.setOnAction(e1->{
                    UiUtil.getScreenCapturePane().show(str->{
                        System.out.println(str);
//                        HtmlEditorUtil.insertHtml(webView, str);
                    });
                });
                HBox hBox = new HBox(label, b3);
                StackPane stackPane = new StackPane(hBox);
//                Scene scene = new Scene(stackPane, Screen.getPrimary().getBounds().getWidth(), Screen.getPrimary().getBounds().getHeight());
                Scene scene = new Scene(stackPane);
                stage.setScene(scene);
                stackPane.setOnMousePressed(ee->{
                    String d = String.format("x:%s, y:%s, w:%s, h:%s", ee.getX(), ee.getY(), stackPane.getWidth(), stackPane.getHeight());
                    label.setText(d);
                    Screen screen = Screen.getPrimary();
                    String t = String.format("w: %s, h:%s", screen.getBounds().getWidth(), screen.getBounds().getHeight());
                    System.out.println("screen size: " + t);
                });
                stackPane.setOnMouseClicked(ee->{
                    if (ee.getClickCount() == 2) stage.close();
                });
                stage.setFullScreen(true);
                stage.show();

            });

            vBox.getChildren().addAll(b1, b2);
            return vBox;
        });

        addTest("TextFlow test", ()->{
            TextFlow textFlow = new TextFlow(
                    new Text("Box with long description that should be wrapped"),
                    new Text("Disconnected from the target VM, address: '127.0.0.1:49311', <br/>transport: 'socket'"),
                    new Text("Disconnected from the target VM, address: '127.0.0.1:49311',<br/> transport: 'socket'"),
                    new Text("Disconnected from the target VM, address: '127.0.0.1:49311', <br/>transport: 'socket'"),
                    new Text("Disconnected from the target VM, address: '127.0.0.1:49311',<br/> transport: 'socket'"),
                    new Text("Connected to the target VM, address: '127.0.0.1:49311', <br/>transport: 'socket'")
            ) {
                @Override
                protected void layoutChildren() {
                    super.layoutChildren();

                    double maxChildWidth = 0;
                    for (Node child : getManagedChildren()) {
                        double childWidth = child.getLayoutBounds().getWidth();
                        maxChildWidth = Math.max(maxChildWidth, childWidth);
                    }
                    double insetWidth = getInsets().getLeft() + getInsets().getRight();
                    double adjustedWidth = maxChildWidth + insetWidth;

                    setMaxWidth(adjustedWidth);
                }
            };

            textFlow.setStyle("-fx-background-color: red");
            textFlow.setMaxWidth(Control.USE_PREF_SIZE);
            textFlow.setMaxHeight(Control.USE_PREF_SIZE);

            Button unclamp = new Button("Unclamp max width");
            unclamp.setOnAction(e -> textFlow.setMaxWidth(Double.MAX_VALUE));

            StackPane wrappedText = new StackPane(textFlow);

            VBox vbox = new VBox(
                    unclamp,
                    wrappedText
            );
            VBox.setVgrow(wrappedText, Priority.ALWAYS);
            return vbox;
        });

        addRichTest();

        addIcons();
    }

    private void addRichTest() {
        addTest("richTextfx", "richTextFx Demo", ()->{
            InlineCssTextArea area = new InlineCssTextArea();
            String alphabet = "abcdefghijklmnopqrstuvwxyz";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 10; i++) {
                sb.append(i).append(" :").append(alphabet).append("\n");
            }
            area = new InlineCssTextArea(sb.toString());

            setupRTFXSpecificCSSShapes(area);

            addExtraCaret(area);

            addExtraSelection(area);

            // select some other range with the regular caret/selection before showing area
            area.selectRange(2, 0, 2, 4);

            area.requestFocus();
            return area;
        });

        addTest("richTextfx", "java Demo", ()->{
            InlineCssTextArea area = new InlineCssTextArea();
            String alphabet = "abcdefghijklmnopqrstuvwxyz";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 10; i++) {
                sb.append(i).append(" :").append(alphabet).append("\n");
            }
            area = new InlineCssTextArea(sb.toString());

            setupRTFXSpecificCSSShapes(area);

            addExtraCaret(area);

            addExtraSelection(area);

            // select some other range with the regular caret/selection before showing area
            area.selectRange(2, 0, 2, 4);

            area.requestFocus();
            return area;
        });
    }

    private void addIcons() {
        addTest("icons", "icons", ()->{
            FlowPane flowPane = new FlowPane();
            flowPane.setHgap(10);
            flowPane.setVgap(10);
            for(FontAwesome fontAwesome : FontAwesome.values()) {
                FontIcon fontIcon1 = FontIcon.of(FontAwesome.findByDescription(fontAwesome.getDescription()), 20, Color.BLACK);
                Label label = new Label();
                label.setGraphic(fontIcon1);

                label.setOnMouseClicked(e->{
                    System.out.println("fontIcon:" + fontAwesome.getDescription());
                });
                flowPane.getChildren().add(label);
            }
            return flowPane;
        });
    }

    private void changeValue(Bounds newValue) {

    }

    private Node buildTop() {
        HBox hBox = new HBox();
        hBox.getStyleClass().add("demo-header");
        Label title = new Label("javafx 测试平台");
        hBox.getChildren().add(title);
        return hBox;
    }

    private void addTest(boolean isShow, String title, Supplier<Node> supplier) {
        String group = isShow ? "" : "隐藏";
        addTest(group, title, supplier);
    }

    private void addTest(String title, Supplier<Node> supplier) {
        addTest("", title, supplier);
    }

    private void addTest(String group, String title, Supplier<Node> supplier) {
        try {
            Button jfxButton = new Button(title);

            jfxButton.setOnAction(e->{
                Node node = supplier.get();
                if (node == null) return;
                contentPane.getChildren().clear();
                contentPane.getChildren().add(node);
            });

            if (group.equals("")) {
                leftPane.getChildren().add(jfxButton);
                return;
            }
            TitledPane titledPane = groupMap.get(group);
            if (titledPane == null) {
                VBox vBox = new VBox(5);
                titledPane = new TitledPane(group, vBox);
                titledPane.setExpanded(false);
                leftPane.getChildren().add(titledPane);
                groupMap.put(group, titledPane);
            }
            VBox box = (VBox) titledPane.getContent();
            box.getChildren().add(jfxButton);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Callback<Activity, Node> listCell() {
        Callback<Activity, Node> callback = activity->{
//            System.out.println("new node:" + activity.getId());
            GridPane gridPane = new GridPane();
            gridPane.getStyleClass().add("list-node");
//            FontIcon fontIcon1 = FontIcon.of(FontAwesome.findByDescription(activity.getIconDescription()), 20, Color.BLACK);
            ImageView imageView = new ImageView(new Image(this.getClass().getResource("/imgs/head.jpg").toExternalForm()));
            imageView.setFitHeight(50);
            imageView.setFitWidth(50);
//            Label iconLabel = new Label();
//            iconLabel.getStyleClass().add("icon-label");
//            iconLabel.setGraphic(imageView);

            Label title = new Label(activity.getTitle());
            title.getStyleClass().add("title-label");
            Label subTitle = new Label(activity.getSubTitle());
            subTitle.getStyleClass().add("subtitle-label");
            gridPane.getChildren().addAll(imageView, title, subTitle);
            int row =0, col=0;
            GridPane.setConstraints(imageView, col++, row, 1, 2);
            GridPane.setConstraints(title, col, row++, 2, 1);
            GridPane.setConstraints(subTitle, col, row, 1, 1);

            return gridPane;
        };
//        activityListView.setCellCallback(callback);

        return callback;
    }

    private List<Activity> loadActivityList(int num) {
        List<Activity> activities = new ArrayList();
        for(int i=0; i<num; i++) {
            Activity activity = new Activity();
            activity.setId(i+1);
            activity.setIconDescription(FontAwesome.SEARCH.getDescription());
            activity.setTitle("demo " + i);
            activity.setSubTitle("sub demo "+ i);
            activity.setLocalDateTime(LocalDateTime.now());

            activities.add(activity);
        }
        return activities;
    }



    private class Activity implements SoftId {
        private Integer id;
        private String title;
        private String subTitle;
        private LocalDateTime localDateTime;

        private String iconDescription;
        private String pictureUrl;

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getSubTitle() {
            return subTitle;
        }

        public void setSubTitle(String subTitle) {
            this.subTitle = subTitle;
        }

        public LocalDateTime getLocalDateTime() {
            return localDateTime;
        }

        public void setLocalDateTime(LocalDateTime localDateTime) {
            this.localDateTime = localDateTime;
        }

        public String getIconDescription() {
            return iconDescription;
        }

        public void setIconDescription(String iconDescription) {
            this.iconDescription = iconDescription;
        }

        public String getPictureUrl() {
            return pictureUrl;
        }

        public void setPictureUrl(String pictureUrl) {
            this.pictureUrl = pictureUrl;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }


        @Override
        public String getSoftId() {
            return id + "";
        }
    }

    private void cleanWeakMap() {
        List<Integer> ids = new ArrayList<>();
        weakMap.forEach((k, v)->{
            if(v == null) ids.add(k);
        });
        ids.forEach(key->weakMap.remove(key));
    }

    private Text buildText(Text tmp) {
        Text t = new Text();
        double w =  0;
        double h =  0;


        return t;
    }

    private void addExtraCaret(InlineCssTextArea area) {
        CaretNode extraCaret = new CaretNode("another caret", area);
        if (!area.addCaret(extraCaret)) {
            throw new IllegalStateException("caret was not added to area");
        }
        extraCaret.moveTo(3, 8);

        // since the CSS properties are re-set when it applies the CSS from files
        // remove the style class so that properties set below are not overridden by CSS
        extraCaret.getStyleClass().remove("caret");

        extraCaret.setStrokeWidth(10.0);
        extraCaret.setStroke(Color.BROWN);
        extraCaret.setBlinkRate(Duration.millis(200));
    }

    private void addExtraSelection(InlineCssTextArea area) {
        Selection<String, String, String> extraSelection = new SelectionImpl<>("another selection", area,
                path -> {
                    // make rendered selection path look like a yellow highlighter
                    path.setStrokeWidth(0);
                    path.setFill(Color.YELLOW);
                }
        );
        if (!area.addSelection(extraSelection)) {
            throw new IllegalStateException("selection was not added to area");
        }
        // select something so it is visible
        extraSelection.selectRange(7, 2, 7, 8);
    }

    /**
     * Shows that RTFX-specific-CSS shapes are laid out in correct order, so that
     * selection and text and caret appears on top of them when made/moved
     */
    private void setupRTFXSpecificCSSShapes(InlineCssTextArea area) {
        String background = "-rtfx-background-color: red; ";
        String underline = "-rtfx-underline-color: blue; " +
                "-rtfx-underline-width: 2.0;";
        String border = "-rtfx-border-stroke-color: green; " +
                "-rtfx-border-stroke-width: 3.0;";

        // set all of them at once on a give line to insure they display properly
        area.setStyle(0, background + underline + border);

        // set each one over a given segment
        area.setStyle(1, 0, 3, background);
        area.setStyle(1, 4, 7, underline);
        area.setStyle(1, 8, 11, border);
    }
}
